namespace Romer
{
    using System;

    public class CRCTool
    {
        private ulong crchighbit;
        private ulong crcinit = 0xffffL;
        private ulong crcinit_direct;
        private ulong crcinit_nondirect;
        private ulong crcmask;
        private ulong[] crctab = SetCrcTab();
        private ulong crcxor;
        private int direct = 1;
        private int order = 0x10;
        public ulong polynom = 0x1021L;
        private int refin;
        private int refout;

        public ushort CalcCRCITT(byte[] p)
        {
            uint num = 0xffff;
            for (int i = 0; i < p.Length; i++)
            {
                uint num2 = (uint) (p[i] << 8);
                for (int j = 0; j < 8; j++)
                {
                    if (((num ^ num2) & 0x8000) != 0)
                    {
                        num = (num << 1) ^ 0x1021;
                    }
                    else
                    {
                        num = num << 1;
                    }
                    num2 = num2 << 1;
                }
            }
            return (ushort) num;
        }

        public ulong crcbitbybit(byte[] p)
        {
            int num;
            ulong num4;
            ulong crc = this.crcinit_nondirect;
            for (num = 0; num < p.Length; num++)
            {
                ulong num3 = p[num];
                if (this.refin != 0)
                {
                    num3 = this.reflect(num3, 8);
                }
                for (ulong i = 0x80L; i != 0L; i = i >> 1)
                {
                    num4 = crc & this.crchighbit;
                    crc = crc << 1;
                    if ((num3 & i) != 0L)
                    {
                        crc |= (ulong) 1L;
                    }
                    if (num4 != 0L)
                    {
                        crc ^= this.polynom;
                    }
                }
            }
            for (num = 0; num < this.order; num++)
            {
                num4 = crc & this.crchighbit;
                crc = crc << 1;
                if (num4 != 0L)
                {
                    crc ^= this.polynom;
                }
            }
            if (this.refout != 0)
            {
                crc = this.reflect(crc, this.order);
            }
            crc ^= this.crcxor;
            return (crc & this.crcmask);
        }

        public ulong crcbitbybitfast(byte[] p)
        {
            ulong crc = this.crcinit_direct;
            for (int i = 0; i < p.Length; i++)
            {
                ulong num3 = p[i];
                if (this.refin != 0)
                {
                    num3 = this.reflect(num3, 8);
                }
                for (ulong j = 0x80L; j > 0L; j = j >> 1)
                {
                    ulong num4 = crc & this.crchighbit;
                    crc = crc << 1;
                    if ((num3 & j) > 0L)
                    {
                        num4 ^= this.crchighbit;
                    }
                    if (num4 > 0L)
                    {
                        crc ^= this.polynom;
                    }
                }
            }
            if (this.refout > 0)
            {
                crc = this.reflect(crc, this.order);
            }
            crc ^= this.crcxor;
            return (crc & this.crcmask);
        }

        public ulong crctable(byte[] p)
        {
            ulong crc = this.crcinit_nondirect;
            if (this.refin != 0)
            {
                crc = this.reflect(crc, this.order);
            }
            if (this.refin == 0)
            {
                for (int i = 0; i < p.Length; i++)
                {
                    crc = ((crc << 8) | p[i]) ^ this.crctab[(int) ((IntPtr) ((crc >> (this.order - 8)) & ((ulong) 0xffL)))];
                }
            }
            else
            {
                for (int j = 0; j < p.Length; j++)
                {
                    crc = (ulong) ((((int) (crc >> 8)) | (p[j] << (this.order - 8))) ^ ((int) this.crctab[(int) ((IntPtr) (crc & ((ulong) 0xffL)))]));
                }
            }
            if (this.refin == 0)
            {
                for (int k = 0; k < (this.order / 8); k++)
                {
                    crc = (crc << 8) ^ this.crctab[(int) ((IntPtr) ((crc >> (this.order - 8)) & ((ulong) 0xffL)))];
                }
            }
            else
            {
                for (int m = 0; m < (this.order / 8); m++)
                {
                    crc = (crc >> 8) ^ this.crctab[(int) ((IntPtr) (crc & ((ulong) 0xffL)))];
                }
            }
            if ((this.refout ^ this.refin) != 0)
            {
                crc = this.reflect(crc, this.order);
            }
            crc ^= this.crcxor;
            return (crc & this.crcmask);
        }

        public ulong crctablefast(byte[] p)
        {
            ulong crc = this.crcinit_direct;
            if (this.refin != 0)
            {
                crc = this.reflect(crc, this.order);
            }
            if (this.refin == 0)
            {
                for (int i = 0; i < p.Length; i++)
                {
                    crc = (crc << 8) ^ this.crctab[(int) ((IntPtr) (((crc >> (this.order - 8)) & ((ulong) 0xffL)) ^ p[i]))];
                }
            }
            else
            {
                for (int j = 0; j < p.Length; j++)
                {
                    crc = (crc >> 8) ^ this.crctab[(int) ((IntPtr) ((crc & ((ulong) 0xffL)) ^ p[j]))];
                }
            }
            if ((this.refout ^ this.refin) != 0)
            {
                crc = this.reflect(crc, this.order);
            }
            crc ^= this.crcxor;
            return (crc & this.crcmask);
        }

        private void generate_crc_table()
        {
            for (int i = 0; i < 0x100; i++)
            {
                ulong crc = (ulong) i;
                if (this.refin != 0)
                {
                    crc = this.reflect(crc, 8);
                }
                crc = crc << (this.order - 8);
                for (int j = 0; j < 8; j++)
                {
                    ulong num3 = crc & this.crchighbit;
                    crc = crc << 1;
                    if (num3 != 0L)
                    {
                        crc ^= this.polynom;
                    }
                }
                if (this.refin != 0)
                {
                    crc = this.reflect(crc, this.order);
                }
                this.crctab[i] = crc & this.crcmask;
            }
        }

        public void Init(CRCCode CodingType)
        {
            ulong num;
            ulong crcinit;
            int num3;
            switch (CodingType)
            {
                case CRCCode.CRC_CCITT:
                    this.order = 0x10;
                    this.direct = 1;
                    this.polynom = 0x1021L;
                    this.crcinit = 0xffffL;
                    this.crcxor = 0L;
                    this.refin = 0;
                    this.refout = 0;
                    break;

                case CRCCode.CRC16:
                    this.order = 0x10;
                    this.direct = 1;
                    this.polynom = 0x8005L;
                    this.crcinit = 0xffffL;
                    this.crcxor = 0L;
                    this.refin = 1;
                    this.refout = 1;
                    break;

                case CRCCode.CRC32:
                    this.order = 0x20;
                    this.direct = 1;
                    this.polynom = 0x4c11db7L;
                    this.crcinit = 0xffffffffL;
                    this.crcxor = 0xffffffffL;
                    this.refin = 1;
                    this.refout = 1;
                    break;
            }
            this.crcmask = (ulong) ((((1L << ((this.order - 1) & 0x3f)) - 1L) << 1) | 1L);
            this.crchighbit = ((ulong) 1L) << (this.order - 1);
            if (this.direct == 0)
            {
                this.crcinit_nondirect = this.crcinit;
                crcinit = this.crcinit;
                for (num3 = 0; num3 < this.order; num3++)
                {
                    num = crcinit & this.crchighbit;
                    crcinit = crcinit << 1;
                    if (num != 0L)
                    {
                        crcinit ^= this.polynom;
                    }
                }
                crcinit &= this.crcmask;
                this.crcinit_direct = crcinit;
            }
            else
            {
                this.crcinit_direct = this.crcinit;
                crcinit = this.crcinit;
                for (num3 = 0; num3 < this.order; num3++)
                {
                    num = crcinit & ((ulong) 1L);
                    if (num != 0L)
                    {
                        crcinit ^= this.polynom;
                    }
                    crcinit = crcinit >> 1;
                    if (num != 0L)
                    {
                        crcinit |= this.crchighbit;
                    }
                }
                this.crcinit_nondirect = crcinit;
            }
        }

        private ulong reflect(ulong crc, int bitnum)
        {
            ulong num2 = 1L;
            ulong num3 = 0L;
            for (ulong i = ((ulong) 1L) << (bitnum - 1); i != 0L; i = i >> 1)
            {
                if ((crc & i) != 0L)
                {
                    num3 |= num2;
                }
                num2 = num2 << 1;
            }
            return num3;
        }

        private static ulong[] SetCrcTab()
        {
            return new ulong[] { 
                0L, 0xc0c1L, 0xc181L, 320L, 0xc301L, 960L, 640L, 0xc241L, 0xc601L, 0x6c0L, 0x780L, 0xc741L, 0x500L, 0xc5c1L, 0xc481L, 0x440L, 
                0xcc01L, 0xcc0L, 0xd80L, 0xcd41L, 0xf00L, 0xcfc1L, 0xce81L, 0xe40L, 0xa00L, 0xcac1L, 0xcb81L, 0xb40L, 0xc901L, 0x9c0L, 0x880L, 0xc841L, 
                0xd801L, 0x18c0L, 0x1980L, 0xd941L, 0x1b00L, 0xdbc1L, 0xda81L, 0x1a40L, 0x1e00L, 0xdec1L, 0xdf81L, 0x1f40L, 0xdd01L, 0x1dc0L, 0x1c80L, 0xdc41L, 
                0x1400L, 0xd4c1L, 0xd581L, 0x1540L, 0xd701L, 0x17c0L, 0x1680L, 0xd641L, 0xd201L, 0x12c0L, 0x1380L, 0xd341L, 0x1100L, 0xd1c1L, 0xd081L, 0x1040L, 
                0xf001L, 0x30c0L, 0x3180L, 0xf141L, 0x3300L, 0xf3c1L, 0xf281L, 0x3240L, 0x3600L, 0xf6c1L, 0xf781L, 0x3740L, 0xf501L, 0x35c0L, 0x3480L, 0xf441L, 
                0x3c00L, 0xfcc1L, 0xfd81L, 0x3d40L, 0xff01L, 0x3fc0L, 0x3e80L, 0xfe41L, 0xfa01L, 0x3ac0L, 0x3b80L, 0xfb41L, 0x3900L, 0xf9c1L, 0xf881L, 0x3840L, 
                0x2800L, 0xe8c1L, 0xe981L, 0x2940L, 0xeb01L, 0x2bc0L, 0x2a80L, 0xea41L, 0xee01L, 0x2ec0L, 0x2f80L, 0xef41L, 0x2d00L, 0xedc1L, 0xec81L, 0x2c40L, 
                0xe401L, 0x24c0L, 0x2580L, 0xe541L, 0x2700L, 0xe7c1L, 0xe681L, 0x2640L, 0x2200L, 0xe2c1L, 0xe381L, 0x2340L, 0xe101L, 0x21c0L, 0x2080L, 0xe041L, 
                0xa001L, 0x60c0L, 0x6180L, 0xa141L, 0x6300L, 0xa3c1L, 0xa281L, 0x6240L, 0x6600L, 0xa6c1L, 0xa781L, 0x6740L, 0xa501L, 0x65c0L, 0x6480L, 0xa441L, 
                0x6c00L, 0xacc1L, 0xad81L, 0x6d40L, 0xaf01L, 0x6fc0L, 0x6e80L, 0xae41L, 0xaa01L, 0x6ac0L, 0x6b80L, 0xab41L, 0x6900L, 0xa9c1L, 0xa881L, 0x6840L, 
                0x7800L, 0xb8c1L, 0xb981L, 0x7940L, 0xbb01L, 0x7bc0L, 0x7a80L, 0xba41L, 0xbe01L, 0x7ec0L, 0x7f80L, 0xbf41L, 0x7d00L, 0xbdc1L, 0xbc81L, 0x7c40L, 
                0xb401L, 0x74c0L, 0x7580L, 0xb541L, 0x7700L, 0xb7c1L, 0xb681L, 0x7640L, 0x7200L, 0xb2c1L, 0xb381L, 0x7340L, 0xb101L, 0x71c0L, 0x7080L, 0xb041L, 
                0x5000L, 0x90c1L, 0x9181L, 0x5140L, 0x9301L, 0x53c0L, 0x5280L, 0x9241L, 0x9601L, 0x56c0L, 0x5780L, 0x9741L, 0x5500L, 0x95c1L, 0x9481L, 0x5440L, 
                0x9c01L, 0x5cc0L, 0x5d80L, 0x9d41L, 0x5f00L, 0x9fc1L, 0x9e81L, 0x5e40L, 0x5a00L, 0x9ac1L, 0x9b81L, 0x5b40L, 0x9901L, 0x59c0L, 0x5880L, 0x9841L, 
                0x8801L, 0x48c0L, 0x4980L, 0x8941L, 0x4b00L, 0x8bc1L, 0x8a81L, 0x4a40L, 0x4e00L, 0x8ec1L, 0x8f81L, 0x4f40L, 0x8d01L, 0x4dc0L, 0x4c80L, 0x8c41L, 
                0x4400L, 0x84c1L, 0x8581L, 0x4540L, 0x8701L, 0x47c0L, 0x4680L, 0x8641L, 0x8201L, 0x42c0L, 0x4380L, 0x8341L, 0x4100L, 0x81c1L, 0x8081L, 0x4040L
             };
        }

        public enum CRCCode
        {
            CRC_CCITT,
            CRC16,
            CRC32
        }
    }
}

